package run.bottle.app.handler;


import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.group.ChannelGroup;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.HttpHeaderNames;
import io.netty.handler.codec.http.websocketx.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.StringUtils;
import run.bottle.app.model.protocol.RequestMessage;
import run.bottle.app.model.protocol.ResponseMessage;
import run.bottle.app.model.dto.UserDTO;
import run.bottle.app.model.support.BottleConst;
import run.bottle.app.utils.AvatarGenerator;
import run.bottle.app.utils.BottleUtils;
import run.bottle.app.utils.JsonUtil;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Map;

/**
 * netty服务端处理器
 *
 * @author lycheng
 * @date 2020/9/24 9:36
 */
@Slf4j
public class WebSocketServerHandler extends SimpleChannelInboundHandler<Object> {

    private static final String WEBSOCKET_PATH = "/ws";

    private WebSocketServerHandshaker handshaker;

    private static final ChannelGroup channels = BottleConst.CHANNELS;

    private static final Map<String,UserDTO> users = BottleConst.USER;

    /**
     * 客户端连接会触发
     */
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        log.info("Channel active......");
    }

    /**
     * 客户端发消息会触发
     */
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception {
        if (msg instanceof FullHttpRequest) {
            handleHttpRequest(ctx, (FullHttpRequest) msg);
        } else if (msg instanceof WebSocketFrame) {
            handleWebSocketFrame(ctx, (WebSocketFrame) msg);
        }
    }

    @Override
    public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
        String channelId = ctx.channel().id().toString();
        String avatar = AvatarGenerator.BASE64_PREFIX;
        try {
            avatar += AvatarGenerator.createBase64Avatar(Math.abs(ctx.channel().id().hashCode()));
        } catch (IOException e) {
            e.printStackTrace();
        }
        UserDTO userDTO = new UserDTO(avatar, channelId,"", channelId);
        BottleUtils.addToGroup("5adacd", userDTO, ctx.channel());
        log.info(ctx.channel().id() + " 加入聊天室,在线人数: " + channels.size());

        log.info("在线用户：{}", BottleConst.USER.size());
        log.info("群组统计：{}", BottleConst.GROUP_CHANNER.size());
    }

    @Override
    public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
        users.remove(ctx.channel().id().toString());
        log.info(ctx.channel().id() + " 离开了聊天室,在线人数: " + channels.size());
    }

    /**
     * 发生异常触发
     */
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        log.info(ctx.channel().id() + " 发生异常:" + cause.getMessage());
        ctx.close();
    }

    private void handleHttpRequest(ChannelHandlerContext ctx, FullHttpRequest req) {
        // Handshake
        WebSocketServerHandshakerFactory wsFactory = new WebSocketServerHandshakerFactory(
                getWebSocketLocation(req), null, true, 5 * 1024 * 1024);
        handshaker = wsFactory.newHandshaker(req);
        if (handshaker == null) {
            WebSocketServerHandshakerFactory.sendUnsupportedVersionResponse(ctx.channel());
        } else {
            handshaker.handshake(ctx.channel(), req);
        }
    }

    private void handleWebSocketFrame(ChannelHandlerContext ctx, WebSocketFrame frame) {
        if (frame instanceof PingWebSocketFrame) {
            ctx.write(new PongWebSocketFrame(frame.content().retain()));
            return;
        }
        if (frame instanceof TextWebSocketFrame) {
            TextWebSocketFrame textWebSocketFrame = (TextWebSocketFrame) frame;
            log.info("服务器收到文本消息: {}", textWebSocketFrame.text());

            RequestMessage sendMessage = (RequestMessage) JsonUtil.jsonToBean(textWebSocketFrame.text(), RequestMessage.class);
            System.out.println(sendMessage);

            if (sendMessage != null && !StringUtils.isEmpty(sendMessage.getTo())) {
                BottleConst.GROUP_CHANNER.get(sendMessage.getTo()).forEach(channel -> {
                    UserDTO userDTO = BottleConst.USER.get(ctx.channel().id().toString());
                    //群发消息
                    ResponseMessage responseMessage = new ResponseMessage();
                    responseMessage.setContent(sendMessage.getContent());
                    responseMessage.setCreateTime(System.currentTimeMillis());
                    responseMessage.setType("text");
                    responseMessage.setUser(userDTO);
                    if (ctx.channel() == channel) {
                        userDTO.set_id("bottle_self");
                    } else {
                        userDTO.set_id(channel.id().toString());
                    }
                    System.out.println(responseMessage);
                    TextWebSocketFrame resText = new TextWebSocketFrame(JsonUtil.objectToJson(responseMessage));
                    channel.writeAndFlush(resText);
                });
            }
            return;
        }
        if (frame instanceof BinaryWebSocketFrame) {
            BinaryWebSocketFrame binary = (BinaryWebSocketFrame) frame;
            log.info("服务器收到二进制消息: {}", binary.content().toString(StandardCharsets.UTF_8));
            channels.forEach(channel -> channel.writeAndFlush(binary.retain()));
        }
    }

    private static String getWebSocketLocation(FullHttpRequest req) {
        String location =  req.headers().get(HttpHeaderNames.HOST) + WEBSOCKET_PATH;
        return "ws://" + location;
    }

    /**
     * 系统推送消息
     * @param msg 消息文本
     */
    public void sendGroup(String msg){
        channels.forEach(channel -> channel.writeAndFlush(new TextWebSocketFrame("系统消息：" +msg)));
    }
}
